package com.cgs.query.remote.netty

import com.cgs.query.exception.QueryException
import com.cgs.query.handler.Request
import com.cgs.query.handler.Response
import mu.KotlinLogging
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.*
import java.util.concurrent.locks.ReentrantLock
import java.util.concurrent.atomic.AtomicInteger

private val logger = KotlinLogging.logger { }

class ResponseFuture(private val request: Request) : Future<Response> {

    companion object {

        val FUTURES = ConcurrentHashMap<Long, ResponseFuture>()
        fun received(response: Response) {
            val future = FUTURES.remove(response.requestId)
            if (future != null) {
                future.doReceived(response)
            } else {
                logger.warn("The timeout response finally returned at "
                        + SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(Date()) + ", response " + response
                        + response.requestId)
            }
        }
    }

    init {
        FUTURES[request.requestId] = this
    }

    private val id: Long = request.requestId
    private var response: Response? = null
    private val state = AtomicInteger(0)// ready ok cancel
    private val lock = ReentrantLock()
    private val done = lock.newCondition()


    override fun cancel(mayInterruptIfRunning: Boolean): Boolean {
        val errorResult = Response(id, null)
        errorResult.exception = QueryException("执行被取消")
        response = errorResult
        FUTURES.remove(id)
        state.getAndSet(3)
        return true
    }

    override fun isCancelled(): Boolean {
        return state.get() == 2
    }

    override fun isDone(): Boolean {
        return response != null && state.get() == 1
    }

    private fun doReceived(res: Response) {
        lock.lock()
        try {
            response = res
            state.set(1)
            done?.signal()
        } finally {
            lock.unlock()
        }
    }

    @Throws(InterruptedException::class, ExecutionException::class)
    override fun get(): Response {
        lock.lock()
        try {
            while (!isDone) {
                done.await()
                if (isDone) {
                    break
                }
            }
        } catch (e: InterruptedException) {
            throw RuntimeException(e)
        } finally {
            lock.unlock()
        }
        return returnFromResponse()
    }

    @Throws(InterruptedException::class, ExecutionException::class, TimeoutException::class)
    override fun get(timeout: Long, unit: TimeUnit): Response {
        var timeout = timeout

        if (timeout <= 0) {
            timeout = 2000L
        }
        if (!isDone) {
            val start = System.currentTimeMillis()
            lock.lock()
            try {
                while (!isDone) {
                    done.await(timeout, unit)
                    if (isDone || System.currentTimeMillis() - start > timeout) {
                        break
                    }
                }
            } catch (e: InterruptedException) {
                throw RuntimeException(e)
            } finally {
                lock.unlock()
            }
            if (!isDone) {
                throw TimeoutException("读取超时：$timeout ")
            }
        }
        return returnFromResponse()
    }

    private fun returnFromResponse(): Response {
        if (response == null) {
            throw QueryException("返回结果不能为null")
        }
        return response!!
    }

}